package planetrussOpt;

import data.DataTest;
import randomsearch_masterworker.RSMaster;
import randomsearch_masterworker.RSWorker;
import mpi.MPI;
import model.PlaneTruss;
import inf.minife.fe.Model;

/* 
 * Basic operation is simple:
 * The set of all possible cross section areas is fed to programm from gadata.dat file.
 * Then contrains on max stress and max displacement are provided.
 * The program does structural analysis on plane truss in PlaneTruss.java class.
 * Next, Random search is called and parallel executed in master-worker paradigm
 * with number of random cycles.
 * In each random values generated by worker, they will be fetch to plane truss model to analysis
 * then result including: new mass value, fitness value of generation and number of
 * violation constrains will be send to master.
 * Master print out the result, then do the analysis to find best generation that fits to model
 * based on fitness value and mass value.
 */

/*
 * The objection function is mass value of model:
 * F(x) = Sum(rho*Aj*Lj)
 * design values is cross section area of 10 truss elements
 * Aj with j = 1..10
 * that is included taken as discreted values from
 * American Institute of Steel Construction Manual.
 */
public class PTRSOpt {
	/* Set up values for design optimize
	 * Sigma(stress) max, displacement max
	 */
	final double sigmaMax_ = 25; // 25 ksi = 172.25 MPa
	final double uMax_= 2; // 2 inch = 50.8 mm
	
	/* Number of possible cross section area value in table */
	final int ncross_section = 42;	//default number of values will be read	= 42
	
	/*
	 * x array store design values
	 */
	
	double[] x;
	
	/*
	 * In this program, we use plane truss model
	 * with 10 truss elements and 6 nodes
	 * follows the model of Rajeev and Krishnamoorthy
	 * "Discrete optimization of structure using genetic algorithm"
	 */
	PlaneTruss planetruss_;
	Model model;
	
	public static void main (String[] args){
		new PTRSOpt(args);
	}
	
	public PTRSOpt(String[] args){
		/* Values for MPI */ 
		int myid;
		int nprocs;
		
		/* Setup model */
		planetruss_ = new PlaneTruss();
		model = planetruss_.getModel();
		

		/* Add master component */		
		RSMaster rsmaster = new RSMaster();
		
		/* number of variables for design 
		 * cross section area of 10 elements
		 */
		
		int design_number = 10; 
		
		/*
		 * Number of result that worker will send to master 
		 * result including: new mass value, fitness value of generation and number of
		 * violation constrains
		 */
		int result_number = 4;
		
		/* Number of cycles for random search */
		int number_cycles = 200;
		
		/* Initiating MPI by starting master */	
		rsmaster.startMPI(args);
		myid = rsmaster.getID();
		nprocs = rsmaster.getNumberOfProcessors();
		
		/* Set up number of workers */
		rsmaster.setNumberOfWorker(nprocs-1);
		MPI.COMM_WORLD.Barrier();
		
		/* Call master or worker code as appropriate */
		
		if (myid == 0){				
			
			/* Start apply model plane truss */
			try{
				model.solve();
				
				/* Calculate mass value of default plane truss */
				double massvalue = model.getTotalMass();
				System.out.println("Init total mass = " + massvalue);
				callMaster(rsmaster.getNumberOfWorkers(), rsmaster, result_number, number_cycles);
			}
			catch (NullPointerException e) {
				System.out.println("Null pointer Exception for plane truss! Init it before use ");
				return;
			}
			MPI.COMM_WORLD.Barrier();
		}
		else{			
			
			try{				
				callWorker(myid, design_number, sigmaMax_, uMax_, planetruss_, result_number);
			}
			catch (NullPointerException e) {
				System.out.println("Null pointer Exception for plane truss! Init it before use ");
				return;
			}
			MPI.COMM_WORLD.Barrier();
		}
		MPI.Finalize();
	}

	

	private void callMaster(int numberOfWorkers, RSMaster rsmaster,
			int result_number, int number_cycles) {		
		
		
		double[] list_area;
		int[] n = new int[ncross_section];
		
		/* Number of generation for GA */
		int masterID = 0;	
		
		/* Get cross section areas from file */
		list_area = rsmaster.prepareWork(n, ncross_section, "../Sourcecode_Week6/src/cross_section_table.dat");
		
		/* Patch data to broadcast to worker */		
		DataTest[] listbcast = new DataTest[list_area.length];
		
		try{
			for (int i = 0; i < list_area.length; i++){
				double temp = list_area[i];
				//System.out.println("Cross section area [" + i + "] = " + list_area[i]);
				listbcast[i] = new DataTest(temp);
			}
		}
		catch (NullPointerException e) {
			System.out.println("Array has null pointer! Init before use");
			return;
		}
		/*
		 * Master broadcast the list of
		 * cross section areas to all workers
		 */
		MPI.COMM_WORLD.Bcast(listbcast, 0, listbcast.length, MPI.OBJECT, masterID);		
		
		/* Start timing */
		rsmaster.startTime();
		
		/* File to store result */
		String filename = "../SourceCode_Week7/src/planetruss_rs_result.dat";
		
		/* Start parallel work assignment */
		rsmaster.doParallel(masterID, numberOfWorkers, result_number, number_cycles, filename);
		
		/* End timing */
		rsmaster.endTime();
		
		double[] starttime = rsmaster.getStarTime();
		double[] endtime = rsmaster.getEndTime();
		
		/* Print out */
		System.out.println("\n Number of workers = " + numberOfWorkers);
		
		//System.out.println("\n Maximum iteration = " + maxiter);
		
		System.out.println("\n Execution MPI time = " + (endtime[0]-starttime[0]));
		System.out.println("\n Execution system time = " + (endtime[1]-starttime[1])/1000);
	}
	
	private void callWorker(int myid, int design_number, double sigmaMax,
			double uMax, PlaneTruss planetruss, int result_number) {
		// TODO Auto-generated method stub
		int master = 0;
		
		/* Start worker */
		RSWorker rsworker = new RSWorker(myid);	
		
		/* Number of posible values for cross section list
		 * = 2^number of chomosome that used to
		 * represent a value in list		
		 */
		
		/* Worker receive the list broad cast from master */
		DataTest[] listbcast = new DataTest[ncross_section];
		
		MPI.COMM_WORLD.Bcast(listbcast, 0, listbcast.length, MPI.OBJECT, master);

		
		/* Call worker do parallel work */
		rsworker.doWork(myid, design_number, master, listbcast, planetruss, sigmaMax, uMax, result_number);
	}
}
